基于Kbone使用React同构小程序开发实践 您所在的位置:网站首页 react 条件编译 基于Kbone使用React同构小程序开发实践

基于Kbone使用React同构小程序开发实践

2022-06-07 23:08| 来源: 网络整理| 查看: 265

原文来自「微信开发者」公众号,本文作者为 CSIG howenhuo,经 WXG binniexu 编辑。 本文主要介绍了基于Kbone使用React同构小程序开发实践: - 两种同构方案及区别; - 如何接入。

1 - 背景

“新人课程礼包”是腾讯课堂的活动需求之一,该需求中有一个礼包课程领取页在多端(小程序、H5、APP)都有涉及。

在小程序端我们可以使用 web-view 嵌入 H5,但该方案加载耗时以及无法使用微信特有的能力(例如:获取微信用户绑定的手机号,沉浸式状态栏),适逢 Kbone 已支持 React 同构,因此我们针对该页面尝试基于 Kbone 使用 React 同构小程序实践。

2 - 框架选择

目前同构的方案有静态编译和适配器两种。我们来看下这两种方案的区别。

> 静态编译

静态编译只是让我们使用 React 和 JSX 的语法来编写小程序代码,然后通过语法分析工具把代码翻译成小程序模板。

由于 JSX 并非模板语言,要将其翻译成小程序模板,则必须要牺牲一些 JS 的动态特性,这也就是为什么这种方案在编写上有很多限制,其本质缺陷在于语法分析是静态的,而 JS 是动态的。

此外,这种方案实际运行时并非 “真 React ”,因此对于跟进 React 特性来说,无法做到与官方同步。

> 适配器(Kbone)

至于 Kbone ,它能够支持完整的 React 和 JSX 语法,是因为它把 React 给完整引入进来,而对于 React 底层依赖了的 dom/bom 接口,它提供一套轻量的小程序适配层接口。

正是因为通过提供适配器的方式来仿造出 Web 环境,所以我们可以在任意位置任意方式书写 React 和 JSX,而无须担心是否不支持某些新特性。

3 - 接入

礼包课程领取页主要涉及到两个现存的工程:

m-core:腾讯课堂 H5 页面,技术栈是 Webpack 4 + Babel 7+ React 16.8 + Typescript。weapp-ke:腾讯课堂小程序,技术栈是小程序原生框架。

为了优先保证 H5 能够正常运行,我们将新页面的代码放到 m-core 项目,接着增加webpack.mp.config.js 配置,由于同构生成的小程序页面依赖 Kbone 的适配层库,为避免原小程序工程主包过大,我们需要构建生成分包页面。

3.1 - 构建配置

我们基于 kbone-template-react 提供的 webpack.mp.config.js 来修改,以支持项目中使用的 React、Typescript、PostCSS、条件编译、Tree Shaking 等特性,还有与小程序代码复用。

> Babel

以下是 H5 和小程序代码转换的公用规则,依据 isMp 来区分不同的转换处理:

{ test: /\.(ts|js)x?$/, use: [ 'thread-loader', { loader: 'babel-loader?cacheDirectory', options: isMp ? { configFile: false, // 避免babel加载babel.config.js presets: [ '@babel/preset-typescript', // 支持typescript'@babel/preset-react', // 支持react ], plugins: [ '@babel/plugin-proposal-class-properties', ] } : { ... }, }, ... ] }

同构小程序使用的 babel-loader 配置与一般 H5 使用的配置有些不同,对于小程序我们可以不加 @babel/preset-env ,是因为小程序开发者工具本身提供 ES6 转 ES5 的代码编译能力和增强编译能力。

对于以下两个插件,不推荐在Kbone中使用:

@babel/plugin-transform-runtime 会导致小程序开发者工具运行报错,@babel/plugin-transform-modules-commonjs 会影响 Webpack 的 Tree Shaking。

此外,我们使用到 webpack-strip-block,目的就是根据环境移除不必要的代码块(效果与 DefinePlugin 相同,但 DefinePlugin 无法处理 import 声明),配合 DefinePlugin 和 Tree Shaking 一起使用。

> Tree Shaking

由于小程序对包大小有严格限制,因此我们需要尽可能地减少包大小。Tree Shaking 是一种代码优化技术,它可以消除那些无用的代码。

在同构项目中我们需要注意以下几点:

tsconfig.json 中的 compilerOptions.module 切勿设置为 “CommonJS”。@babel/preset-env 的 modules 切勿设置为 “commonjs”,同时避免使用 @babel/plugin-transform-modules-commonjs。如果项目中 H5 部分使用了某些自执行的模块而无法使用 Tree Shaking,那么我们可以仅在构建小程序的配置中使用 Module.Rule.sideEffects 开启 Tree Shaking 而不影响 H5 的构建。

> 与小程序代码复用

现有 weapp-ke 小程序工程中使用了 @tencent/imwxutils 等 npm 库以及实现了各种 utils 代码,如果在同构代码中再实现一遍显然是再造轮子,同时会增加小程序包的大小,因此我们需要复用 weapp-ke 已有的代码。

对于 npm 包,由于 weapp-ke 小程序主包已经引入,所以同构代码在构建小程序代码时只需要通过 Webpack 的 externals 将 npm 包从输出的代码中排除,这样小程序在运行时会去主包获取这些依赖。对于 weapp-ke 小程序已实现的 utils 代码,首先我们需要定位到 utils 目录并给它取一个别名,例如 weappKeUtils。resolve.alias.weappKeUtils = path.resolve(weappKeDir, 'utils');

接着我们在同构的代码中引用小程序的代码模块。

import * as keRouter from 'weappKeUtils/router'; export function refresh() { if (process.env.isMiniprogram) { keRouter.refresh(); } else { window.location.reload(); } }

最后通过 Webpack 的 externals 将 weappKeUtils 代码模块以相对路劲的方式外部依赖到小程序中。

externals: [ (context, request, callback) => { if (/^weappUtils/.test(request)) { // 通过commonjs引入小程序项目的utils,小程序模块定位只能使用相对路径 return callback(null, request.replace('weappUtils', '../../../utils'), 'commonjs'); } return callback(); } ]

3.2 - 代码编写

> 小程序、H5 公共库适配

由于原本的 H5 和小程序项目是分开的,暂时没有统一的模块管理。尤其是涉及两端特有 api 的库无法共用。可以考虑抽出一个适配层,抹平两端公共库的差异,如 request、上报组件、统一路由跳转等。

举个例子,由于同构的代码在 H5 项目中,我们将小程序的 request 方法向 H5 的 request 方法对齐入参和返回值,进行适配。

/* strip-block--h5-only:end */ request = function mpRequest(url: string, options: any = {}) { // ... } /* strip-block--h5-only:end */ /* strip-block--h5-only:begin */ request = require('assets/request').default; /* strip-block--h5-only:end */ export default request;

> open-type button 的回调函数

React 有一套自己的事件系统,用事件委托的方法,在 document 对事件进行监听。因此我们在 JSX 中所传入的若不是 React 支持的 DOM 事件(如 click、mouseenter),DOM 上是获取不到我们传入的回调方法的。

而在小程序中,对于部分设置了 open-type 的 button,小程序支持设置回调来获取一些用户授权的信息,如 对应了 bindgetphonenumber 回调,我们从在回调中获得解密用户手机号码的参数。这些都不是 React 中支持的回调函数。

因此这些方法需要被手动绑定到 DOM 上,才能被 Kbone 获取并触发到。我们可以另外封装一个 WxButton 组件,对这种特殊的回调做处理:

processWxEvents = (fn: any) => { if (process.env.isMiniprogram) { Object.keys(this.props).forEach((k) => { if (k.indexOf('wxOn') === 0) { const eventName = `on${k.slice(4)}`; fn(eventName, this.props[k]); } }); } } componentDidMount() { this.processWxEvents(this.addEventToDom); } componentWillUnmount() { this.processWxEvents(this.removeEventFromDom); } render() { const { children, className } = this.props; return ( {children} );

 > 小程序组件 boolean 类型的属性

小程序组件有时候需要传递 boolean 类型的参数,如 的 lazy-load 属性,直接在 JSX 中书写,属性也无法被 Kbone 读取到,可以换种方式,通过类型转换来传递 boolean 类型的属性:

4 - 总结

就目前 Kbone 实现的同构小程序效果来看还是不错的:

开发体验:低成本接入现有 H5 项目,并只需要针对 process.env.isMiniprogram 做小程序端特有的逻辑,其他完全与开发 H5 无异。性能质量:由于实践的页面结构相对简单,所以流畅性基本可以与原生开发一样。后续实践将会针对结构复杂的页面研究其性能。

最终实现效果大家可以长按二维码/小程序码体验~

如果你对 Kbone 感兴趣

欢迎在 Github 提出 Issue 和 PR !

关于 Kbone-API 你有什么使用问题或建议

欢迎访问 #Kbone官方框架 社区发帖交流



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有